<?php
/**
 * ------------------------------------
 * # 上传工具类
 * ------------------------------------
 * 1. 2020年6月21日15:35:41
 * 2. ZJY
 * ------------------------------------
 */
class Uploader
{
    # 自变量
    ## 上传路径
    public $upload_path;
    ## 允许上传文件类型
    public $file_types = array('png', 'jpg', 'jpeg', 'gif');
    ## 上传子目录类型：0 不开启、1 开启为格式 Ymd/ 默认、2 开启为格式 Y/m/d/
    public $sub_path_type = 1;
    # 因变量
    ## 上传子目录
    private $sub_path;
    ## 上传完整路径
    private $upload_full_path;
    # 其他变量
    ## 子目录可选类型
    private $sub_path_type_text = array(
        0 => '',
        1 => 'Ymd/',
        2 => 'Ym/d/',
        3 => 'Y/md/',
        4 => 'Y/m/d/',
        5 => 'Ym/',
        6 => 'Y/m/',
    );
    ## 上传文件错误信息
    private $error=array(
        0 => '上传成功',
        1 => '上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值',
        2 => '上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值',
        3 => '文件只有部分被上传',
        4 => '没有文件被上传',
        5 => '上传文件的类型未指定', # 此错误码已被官方废弃，本上传类用于所上传的文件类型被禁止
        6 => '找不到临时文件夹',
        7 => '文件写入失败',
    );
    // 初始化
    public function __construct($upload_path = null)
    {
        // 设置上传路径
        $this -> setUploadPath($upload_path);
        // 设置允许上传文件的类型
        $this -> setFileTypes();
        // 设置上传子目录类型
        $this -> setSubPathType();
    }
    // 设置上传路径
    public function setUploadPath($upload_path = null)
    {
        $this -> upload_path = $upload_path ?: realpath('./').'/uploads';
    }
    // 设置允许上传文件类型
    public function setFileTypes($file_types = null)
    {
        if(is_string($file_types)){
            $this -> file_types = explode(',', $file_types);
        }
        $this -> file_types = $file_types ?: $this -> file_types;
    }
    // 设置上传子目录类型
    public function setSubPathType($sub_path_type = null)
    {
        $this -> sub_path_type = isset($sub_path_type) && key_exists($sub_path_type, $this -> sub_path_type_text) ? $sub_path_type : $this -> sub_path_type;
    }
    // 设置上传子目录路径
    private function setSubPath()
    {
        $this -> sub_path = $this -> sub_path_type ? date($this -> sub_path_type_text[$this -> sub_path_type]) : '';
    }
    // 设置完整的上传路径，若目录不存在则创建
    private function setUploadFullPath()
    {
        $result = true;
        // 文件上传路径
        $upload_full_path = $this -> upload_path . '/' . $this -> sub_path;
        if (!is_dir($upload_full_path)){
            $result = mkdir($upload_full_path, 0755, true);
        }
        $this -> upload_full_path = $upload_full_path;
        return $result;
    }
    // 检测上传文件类型
    public function checkFileType($file_type)
    {
        return in_array($file_type, $this -> file_types);
    }
    // 重新格式化上传文件数组
    public function resetFILES($files = [])
    {
        $result=false;
        if(!is_array($files) && count($files) == 0){
            return $result;
        }
        foreach($files as $item => $vo){
            // 判断上传文件数组的类型
            if(is_array($vo['error'])){
                foreach($vo as $k => $v){
                    // 分散各组值到相应的数组中
                    foreach($v as $key=>$value){
                        $key_temp = $item.'_'.$key;
                        $result[$key_temp][$k] = $value;
                    }

                }
            }else{
                $result[$item] = $vo;
            }

        }
        return $result;
    }
    // 上传多个文件 返回值：数组 文件名称、文件相对路径、回执信息、上传结果
    public function uploads($files)
    {
        $result=array(
            'status' => 0,
            'info' => '上传失败',
            'data' => [],
        );
        // 判断传入参数
        if(!is_array($files)){
            $result['info'] = '上传失败，传参有误、此参数需要为数组';
            return $result;
        }
        // 格式化参数
        $files = $this -> resetFILES($files) ?: [];
        // 定义上传总状态
        $status = 0;
        foreach($files as $k => $v){
            $ret = $this -> uploadOne($v);
            $result['data'][$k] = $ret;
            $status = $status + $ret['status'];
        }
        $result['status'] = $status;
        $result['info'] = $status ? '上传成功' : '上传失败';
        return $result;
    }
    // 上传单个文件 返回值：文件名称、文件相对路径、回执信息、上传结果
    public function uploadOne($files)
    {
        $result = array(
            'status' => 0,
            'info' => '上传失败',
            'file_ext' => '',
            'file_name' => '',
            'file_path' => '',
        );
        // 设置子目录
        $this -> setSubPath();

        // 设置上传目录
        $set_upload_full_path_result = $this -> setUploadFullPath();
        if (!$set_upload_full_path_result) {
            $result['info'] = '上传失败，上传目录创建失败';
            return $result;
        }

        // 判断传参是否正确
        if(!isset($files['error']) || is_array($files['error'])){
            $result['info'] = '上传失败，请使用批量上传方法';
            return $result;
        }

        // 系统检测是否上传成功
        if($files['error'] !== 0 || $files['size'] === 0){
            $result['info'] = $this -> error[$files['error']] ?: '上传失败';
            return $result;
        }

        // 获取扩展名、检测文件类型、定义文件新名称
        $file_extra = pathinfo($files['name'],PATHINFO_EXTENSION);
        $check_file_type_result = $this -> checkFileType($file_extra);
        if(!$check_file_type_result){
            $result['info'] = $this -> error[5];
            return $result;
        }
        $file_name = uniqid().'.'.$file_extra;
        // 文件全路径
        $file_full_name = $this -> upload_full_path.$file_name;
        // 上传文件
        $ret = move_uploaded_file($files['tmp_name'], $file_full_name);
        if($ret){
            $result['status'] = 1;
            $result['info'] = '上传成功';
            $result['file_ext'] = $file_extra;
            $result['file_name'] = $file_name;
            $result['file_path'] = $this -> sub_path.$file_name;
        }
        return $result;
    }
    // 不能设置因变量，同时消除设置因变量出错的信息
    public function __set($key, $value)
    {
        return;
    }
    // 获取因变量
    public function __get($key)
    {
        return $this -> $key;
    }
}
